package beaver.game;

import java.io.*;
import java.net.*;
import java.util.LinkedList;
import java.util.Queue;

import org.newdawn.slick.Input;

public class GameServer{
	private ServerSocket server;

	private Player p1;
	private Player p2;
	private int connectedPlayers;
	private volatile Player currentPlayer;
	private float[][] terrainBitmap;
	
	private boolean shutDown = false;
	private Socket client1;
	private Socket client2;
	
	private volatile int movesVersion = 0;
	private volatile Queue<String> p1Moves = null;
	private volatile Queue<String> p2Moves = null;

	
	private DataOutputStream c1Out = null;
	private DataInputStream c1In = null;
	private ObjectOutputStream obj1Out = null;
	private ObjectInputStream obj1In = null;
	
	private DataOutputStream c2Out = null;
	private DataInputStream c2In = null;
	private ObjectOutputStream obj2Out = null;
	private ObjectInputStream obj2In = null;
	
	private volatile int whoReads = 2;
	private boolean canRead = false;

	
	

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public GameServer(){
		p1Moves = new LinkedList<String>();
		p2Moves = new LinkedList<String>();
		
		connectedPlayers = 0;
		p1 = new Player("Player 1");
		p2 = new Player("Player 2");
		currentPlayer=p1;
		terrainBitmap = Noise.GenerateWhiteNoise(Constants.MAX_WIDTH,Constants.MAX_HEIGHT*2);
		terrainBitmap = Noise.GenerateSmoothNoise(terrainBitmap,7);
		terrainBitmap = Noise.GeneratePerlinNoise(terrainBitmap,7);
		
		try {
			server = new ServerSocket(Constants.SERVER_PORT);
		} catch (IOException e1) {
			e1.printStackTrace();
		}		
		
	}
	
	public void run(){
		Socket newClient = null;
		while(!shutDown){
			try {
				Thread.sleep(Constants.NETWORK_WAIT);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			try {
				newClient = server.accept();
				System.out.println("[INFO] NEW CLIENT CONNECTION");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			Thread t;
			switch(connectedPlayers){
			case 0:
				
				client1 = newClient;
				connectedPlayers++;
				t = new Thread(){
					public void run(){
						handleClient1();
					}
				};
				t.start();
				break;
			case 1:
				client2 = newClient;
				connectedPlayers++;
				t = new Thread(){
					public void run(){
						handleClient2();
					}
				};
				t.start();
				break;
			default:
				try {
					DataOutputStream output = new DataOutputStream(newClient.getOutputStream());
					output.writeUTF("error");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				break;
			}
		}
	}
	
	public void handleClient1(){
		System.out.println("[INFO] PLAYER 1 CONNECTED");
		
		try {
			c1Out = new DataOutputStream(client1.getOutputStream());
			c1In = new DataInputStream(client1.getInputStream());
			obj1Out = new ObjectOutputStream(client1.getOutputStream());
			obj1In = new ObjectInputStream(client1.getInputStream());
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			c1Out.writeUTF("accepted");
			obj1Out.writeObject(p1);
			obj1Out.writeObject(p2);
			obj1Out.writeObject(terrainBitmap);
			c1Out.writeBoolean(true);
			
			while(true){
				Thread.sleep(Constants.NETWORK_WAIT);
				if(c1In.available()==0){
					continue;
				}
				int message = c1In.readInt();
				
				if(message == Constants.END_CONNECTION){
					break;
				}
				
				if(message==Constants.CAN_WRITE_MOVES){
					if(currentPlayer==p1){
						c1Out.writeInt(Constants.YES);
					}
					else{
						c1Out.writeInt(Constants.NO);
					}
				}
				
				if(message==Constants.WRITE_MOVES){
					System.out.println("PLAYER 1 WRITING MOVES TO SERVER");
					p1Moves = (Queue<String>) obj1In.readUnshared();
					
					whoReads = 2;
					canRead = true;
					System.out.println("SERVER READ " + p1Moves.size() + " MOVES");
				}
				
				if(message==Constants.GET_MOVES){
					//System.out.println("SENDING P2 MOVES TO P1");

					int tempMovesVersion = c1In.readInt();
					if(whoReads==1 && canRead){
						obj1Out.writeObject(p2Moves);
						canRead=false;
					}
					else{
						obj1Out.writeObject(null);
					}
					
				}	
				
				if(message==Constants.OPPONENT_TURN_ENDED){
					System.out.println("PLAYER 1 FINISHED PLAYING PLAYER 2 MOVES");
				}
				
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	
	public void sendTestMoves(){
		System.out.println("PLAYER 2 WRITING MOVES TO SERVER");
		Queue test = new LinkedList<String>();
		for(int i = 0; i<1000;i++){
			test.add("down");
		}
		try {
			obj1Out.writeObject(test);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void handleClient2(){
		System.out.println("[INFO] PLAYER 2 CONNECTED");

		try {
			c2Out = new DataOutputStream(client2.getOutputStream());
			c2In = new DataInputStream(client2.getInputStream());
			obj2Out = new ObjectOutputStream(client2.getOutputStream());
			obj2In = new ObjectInputStream(client2.getInputStream());
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			c2Out.writeUTF("accepted");
			obj2Out.writeObject(p2);
			obj2Out.writeObject(p1);
			obj2Out.writeObject(terrainBitmap);
			c2Out.writeBoolean(false);
			while(true){
				Thread.sleep(Constants.NETWORK_WAIT);
				if(c2In.available()==0){
					continue;
				}
				int message = c2In.readInt();
				
				if(message == Constants.END_CONNECTION){
					break;
				}
				
				if(message==Constants.WRITE_MOVES){	
					System.out.println("PLAYER 2 WRITING MOVES TO SERVER");
					p2Moves = (Queue<String>) obj2In.readUnshared();
					whoReads = 1;
					canRead = true;
					System.out.println("SERVER READ " + p2Moves.size() + " MOVES");
				}
				
				if(message==Constants.GET_MOVES){
					//System.out.println("SENDING P1 MOVES TO P2");
					int tempMovesVersion = c2In.readInt();
					if(whoReads==2 && canRead){
						obj2Out.writeObject(p1Moves);
						canRead=false;
					}
					else{
						obj2Out.writeObject(null);
					}
					
				}
				
				if(message==Constants.CAN_WRITE_MOVES){
					if(currentPlayer==p2){
						c2Out.writeInt(Constants.YES);
					}
					else{
						c2Out.writeInt(Constants.NO);
					}
				}
				
				if(message==Constants.OPPONENT_TURN_ENDED){
					System.out.println("PLAYER 2 FINISHED PLAYING PLAYER 1 MOVES");
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static void main(String[] args){
		GameServer server = new GameServer();
		server.run();
	}

}
